#include"../include/parsing_ll1.h"
typedef struct
{
    /* data */
    char*l;
    char*r;
}lr;

/*funcation*/
//比较函数
int compare(const void *a, const void *b) {  
    const lr *lr1 = (const lr *)a;  
    const lr *lr2 = (const lr *)b;  
    int cmp = strcmp(lr1->l, lr2->l);  
    if (cmp != 0) return cmp;  
    return strcmp(lr1->r, lr2->r);  
}

// 去除字符串两侧空白字符
char* trim(char *str) {
    if (str == NULL) return NULL;

    // 去除左侧空白
    char *start = str;
    while (*start && isspace((unsigned char)*start)) {
        start++;
    }

    // 全空白字符串处理
    if (*start == '\0') {
        *str = '\0';
        return str;
    }

    // 去除右侧空白
    char *end = str + strlen(str) - 1;
    while (end > start && isspace((unsigned char)*end)) {
        end--;
    }
    *(end + 1) = '\0';

    // 移动处理后的字符串到开头
    if (start != str) {
        memmove(str, start, end - start + 2); // +2包含终止符
    }

    return str;
}

//初始化预测分析表
void init_table(void *self,const char*filePath,char *toolsPath){
    Parsing_LL1 *p=(Parsing_LL1*)self;
    FILE *fp1 = fopen(filePath, "r");  
    if (!fp1) {  
        perror("文件打开失败");  
        return;  
    }  

    char buffer1[1024] = {0};  
    int lines_read = 0;  

    while (lines_read < 3 && fgets(buffer1, sizeof(buffer1), fp1)) {  
        // 去除换行符  
        buffer1[strcspn(buffer1, "\n")] = '\0';  
    
        // 去除两侧空格  
        trim(buffer1);  
        // 压入栈中  
        if(lines_read<=1)p->table_stack->push(p->table_stack, buffer1);  
        else p->input=strdup(buffer1);
        lines_read++;  
    
        // 清空缓冲区以备下次读取  
        memset(buffer1, 0, sizeof(buffer1));  
    }  

    if (lines_read < 3) {  
        printf("警告: 文件不足3行，只读取了%d行\n", lines_read);  
    }  

    fclose(fp1);  
    
    // 计算各部分长度（注意包含必要的空格和符号）
    size_t totalLen = strlen(toolsPath)       // toolsPath长度
                      + strlen("/ll1_parser ")  // 中间固定部分
                      + strlen(filePath)        // 文件路径参数
                      + strlen(" > output.txt")// 输出重定向部分
                      + 1;                      // 终止符 \0

    // 动态分配内存
    char *fullCommand = (char*)malloc(totalLen);
    if (!fullCommand) {
        perror("内存分配失败");
        exit(EXIT_FAILURE);
    }

    // 安全拼接字符串
    snprintf(fullCommand, totalLen, "%s/ll1_parser %s > output.txt", toolsPath, filePath);


    // 使用完记得释放内存
    system(fullCommand);  
    free(fullCommand);
    // 读取临时文件  
    FILE *fp = fopen("output.txt", "r");  
    if (fp == NULL) {  
        perror("fopen failed");  
        return;  
    }  
    char buffer[1024];  
    char *data[1024];
    char*h_data;
    int i=0;
    int is_one=1;;
    int check=0;
    while (fgets(buffer, sizeof(buffer), fp) != NULL) {  
        printf("%s",buffer);
        if(check){
            const char delim[] = " \t\n";  
            char *token = strtok(buffer, delim);  
            int is_h_one=1;
            int idx=0;
            while (token != NULL && strcmp(token,"\n")==1) { 
                if(is_one) {
                    data[i++] = strdup(token);
                }else{
                    if(is_h_one){h_data=strdup(token);is_h_one=0;}
                    else{
                        if(strcmp(token,"null")){
                            char* saveptr;  
                            char* token_copy = strdup(token);  
                            char* token2 = strtok_r(token_copy, "->", &saveptr);  
                            int is_two=0;
                            while (token2 != NULL) {  
                                if(is_two){  
                                    lr *key = malloc(sizeof(lr));
                                    key->l = strdup(h_data);    // 确保字符串持久化
                                    key->r = strdup(data[idx]);
                                    p->parsingTable->put(p->parsingTable, key, strdup(token2)); 
                                    // printf(">%s<\n",(char*)parsingTable->get(parsingTable,&(lr){"E","i"}));
                                }
                                is_two++;
                                token2 = strtok_r(NULL, "->", &saveptr);  
                            }  

                            free(token_copy);  
                        }
                        idx++;
                    }

                }  
                token = strtok(NULL, delim);  
            }  
            is_one=0;
        }
        if(strcmp(buffer,"非终结符\\终结符")==1){check=1;fgets(buffer, sizeof(buffer), fp);}
    }  

    fclose(fp);  
    remove("output.txt"); // 删除临时文件  

}

void to_parsing(void *self){
    #ifdef _WIN32
    SetConsoleOutputCP(CP_UTF8);  // Windows设置控制台为UTF-8
    #endif
    Parsing_LL1* p=(Parsing_LL1*)self;
    while(!p->table_stack->is_empty(p->table_stack)){
        if(p->input[p->o_index]==' '){p->o_index++;continue;}
        p->line=p->table_stack->back(p->table_stack);
        char column_str[2] = { p->input[p->o_index], '\0' };  // 表列名

        if(strcmp(p->line,column_str)==0){printf("%d=>%s matching!\n",p->idx,p->line);p->idx++;p->o_index++;p->table_stack->pop(p->table_stack);continue;}
        char*key=(char*)p->parsingTable->get(p->parsingTable,&(lr){p->line, column_str}); 
        if(key==NULL){printf("\033[31mAnalysis error!\033[0m");return;}//异常

        printf("%d=>line:%s,column_str:%s,key:%s\n",p->idx,p->line,column_str,key);
        p->idx++;
        if(strcmp(key,"ε")==0){p->table_stack->pop(p->table_stack);continue;}//空过

        int len=strlen(key);
        p->table_stack->pop(p->table_stack);
        for(int i=len-1;i>=0;i--){
            char current_char = key[i];
            char column_str[2] = { current_char, '\0' };  // 局部变量存储字符串
            p->table_stack->push(p->table_stack,column_str);
        }
    }
    printf("\033[32mAnalysis success!\033[0m");
    return;
}

Parsing_LL1* parsing_ll1(){
    Parsing_LL1 *p = malloc(sizeof(Parsing_LL1));  
    printf("\033[34m魔术时间开始了！！！\033[0m\n");
    printf("==================================\n");
    p->parsingTable=map(compare);
    p->table_stack=stack(sizeof(char*));
    p->init_table=init_table;//关键
    p->to_parsing=to_parsing;
    p->idx=1;
    p->o_index=0;
    return p;
}